package com.sitech.ibnms.c3p.util;

import org.apache.ignite.IgniteServices;
import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import static com.sitech.ibnms.c3p.util.C3pConstants.IGNITE_GRID_NAME;
import static org.apache.ignite.IgniteState.STARTED;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_NO_ASCII;

/**
 * Created by leo on 16/3/1.
 */
public class IgniteServiceFactoryBean<T> extends AbstractFactoryBean<T> {
    private IgniteServices igniteServices;

    private DataSource discoveryDataSource;
    private String serviceName;
    private Class<T> serviceInterface;
    private boolean sticky = false;

    private IgniteServices services() {
        if (igniteServices == null) {
            synchronized (IGNITE_GRID_NAME) {
                if (!STARTED.equals(Ignition.state(IGNITE_GRID_NAME))) {
                    System.setProperty(IGNITE_NO_ASCII, "Y");
                    IgniteConfiguration igniteConfiguration = new IgniteConfiguration();
                    igniteConfiguration.setClientMode(true);
                    igniteConfiguration.setGridName(IGNITE_GRID_NAME);
                    igniteConfiguration.setWorkDirectory(System.getProperty("user.home") + "/.c3p-client");
                    if (discoveryDataSource != null) { // 基于数据库的集群发现
                        TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
                        TcpDiscoveryJdbcIpFinder ipFinder = new TcpDiscoveryJdbcIpFinder();
                        ipFinder.setDataSource(discoveryDataSource);
                        discoSpi.setIpFinder(ipFinder);
                        igniteConfiguration.setDiscoverySpi(discoSpi);
                    }
                    Ignition.start(igniteConfiguration);
                }
            }
            igniteServices = Ignition.ignite(IGNITE_GRID_NAME).services();
        }
        return igniteServices;
    }

    @Override
    protected T createInstance() throws Exception {
        Assert.notNull(serviceName, "serviceName must not be null");
        Assert.notNull(serviceInterface, "serviceInterface must not be null");
        //noinspection unchecked
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class[]{serviceInterface}, new InvocationHandler() {
            T target;

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (target == null) {
                    target = services().serviceProxy(serviceName, serviceInterface, sticky);
                }
                return method.invoke(target, args);
            }
        });
    }

    @Override
    public Class<T> getObjectType() {
        return serviceInterface;
    }

    @Override
    public void destroy() throws Exception {
        super.destroy();
        Ignition.stopAll(true);
    }

    public void setDiscoveryDataSource(DataSource discoveryDataSource) {
        this.discoveryDataSource = discoveryDataSource;
    }

    public void setServiceInterface(Class<T> serviceInterface) {
        this.serviceInterface = serviceInterface;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public void setSticky(boolean sticky) {
        this.sticky = sticky;
    }
}
